﻿using System;
using System.Collections.Generic;
using System.Linq;
using Internal.Runtime.Services.BattleAttack;
using KuiHuaBaoDian.Services;
using Runtime.Models;
using Runtime.Services.Battle;
using Runtime.Services.Battle.Entity;
using Runtime.Services.BattleAttack;
using Runtime.Services.BattleRecord;
using Runtime.Services.BattleSkill;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Pool;

namespace Runtime.Services.BattleAttack {

    /// <summary>
    /// 战斗进攻服务
    /// </summary>
    public interface IDFR_BattleAttackSerivce {

        /// <summary>
        /// 索敌
        /// </summary>
        /// <param name="lookout">瞭望者</param>
        /// <returns>观察到的目标列表</returns>
        IEnumerable<IDFR_BattleAttackTarget> Spotting(IDFR_BattleAttackLookout lookout);

        /// <summary>
        /// 选择目标
        /// </summary>
        /// <param name="targets">可用的源数据，一般来源于<see cref="Spotting(IDFR_BattleAttackLookout)"/>提供的列表</param>
        /// <param name="selector">目标选择器</param>
        /// <param name="targetModelDictionary">目标数据集合</param>
        /// <returns>选定的目标列表</returns>
        IEnumerable<IDFR_BattleAttackTarget> Selection(IEnumerable<IDFR_BattleAttackTarget> targets, IDFR_BattleAttackTargetSelector selector, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary);

        /// <summary>
        /// 发起进攻
        /// </summary>
        /// <param name="attacker">进攻者</param>
        /// <param name="skillList">技能列表</param>
        /// <param name="bulletProvider">子弹提供者</param>
        /// <param name="selectedTargets">已选目标列表</param>
        /// <param name="spottingTargets">看到的目标列表</param>
        /// <param name="allTargets">所有目标列表</param>
        void Launch(
            IDFR_BattleAttacker attacker,
            IReadOnlyList<IDFR_BattleSkillModelBase> skillList,
            IDFR_BattleAttackBulletProvider bulletProvider,
            IEnumerable<IDFR_BattleAttackTarget> selectedTargets,
            IEnumerable<IDFR_BattleAttackTarget> spottingTargets,
            IEnumerable<IDFR_BattleAttackTarget> allTargets);

        /// <summary>
        /// 击中目标
        /// </summary>
        /// <param name="snapshot">发起时的快照</param>
        /// <param name="targetEntityInstanceID">击中的目标实例ID</param>
        /// <param name="targetMapPositionProvider">击中的目标位置</param>
        /// <param name="sourceModelDictionary">来源数据集合</param>
        /// <param name="targetModelDictionary">目标数据集合</param>
        void Hit(IDFR_BattleAttackSnapshot snapshot, DFR_BattleEntityInstanceID targetEntityInstanceID, IDFR_BattleMapPositionProvider targetMapPositionProvider,
            IDFR_ReadOnlyBattleAttackSourceDictionaryModelBase sourceModelDictionary, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary);

        /// <summary>
        /// 通过，飞行逻辑帧中使用
        /// </summary>
        /// <param name="snapshot">发起时的快照</param>
        /// <param name="targetEntityInstanceID">主目标实体实例ID</param>
        /// <param name="lastMapPosition">上一帧所处的地图位置</param>
        /// <param name="mapPosition">当前所处的地图位置</param>
        /// <param name="endMapPosition">目标所处的地图位置</param>
        /// <param name="sourceModelDictionary">来源数据集合</param>
        /// <param name="targetModelDictionary">目标数据集合</param>
        /// <returns>如果需要销毁，那么返回 true，否则返回 false</returns>
        bool Through(IDFR_BattleAttackSnapshot snapshot, DFR_BattleEntityInstanceID targetEntityInstanceID,
           DFR_BattleMapPosition lastMapPosition, DFR_BattleMapPosition mapPosition, DFR_BattleMapPosition endMapPosition,
           IDFR_ReadOnlyBattleAttackSourceDictionaryModelBase sourceModelDictionary, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary);

        /// <summary>
        /// 检测点是否在四边形内
        /// </summary>
        /// <param name="rectCenter">地图位置</param>
        /// <param name="rectRange">范围</param>
        /// <param name="point">目标地图位置</param>
        /// <returns> <c>true</c> 表示在四边形内</returns>
        bool IsPointInRectangle(DFR_BattleMapPosition rectCenter, F64 rectRange, DFR_BattleMapPosition point);

        /// <summary>
        /// 受到攻击
        /// </summary>
        /// <param name="snapshot">进攻方发起时的快照</param>
        /// <param name="target">被攻击到的目标</param>
        /// <param name="mainTargetEntityInstanceID">被攻击时针对的主要目标实例ID</param>
        /// <returns> <c>true</c> 表示在<paramref name="target"/>被击杀</returns>
        bool Underattack(IDFR_BattleAttackSnapshot snapshot, IDFR_BattleAttackTarget target, DFR_BattleEntityInstanceID mainTargetEntityInstanceID);
    }

    public static class BattleAttackService {

        /// <summary>
        /// 单例
        /// </summary>
        public static IDFR_BattleAttackSerivce Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IDFR_BattleAttackSerivce>();
        private static IDFR_BattleAttackSerivce m_Instance;
    }

    [Implement(typeof(IDFR_BattleAttackSerivce))]
    internal sealed partial class DFR_BattleAttackService : IDFR_BattleAttackSerivce, IService, IDFR_BattleAttackServiceInternal {

        /// <summary>
        /// 子弹飞行过程产生伤害的方式的碰撞阈值
        /// </summary>
        public static readonly F64 BATTLE_BULLET_FLY_COLLISION_THRESHOLD = F64.From(0.1f);

        /// <summary>
        /// 碰撞检测大小补充值以略微大于目标值
        /// </summary>
        public static readonly F64 BATTLE_COLLISION_RECTANGLE_ADD_SIZE = F64.From(0.01f);

        /// <summary>
        /// 【战斗】子弹击中自动销毁时间
        /// </summary>
        public readonly static F64 BATTLE_HIT_AUTO_KILL_TIME = F64.From(2f);

        private sealed class HitTarget : IDFR_BattleRecordHitTarget {

            public bool IsKill { get; set; }

            public IDFR_BattleRecordService.KillType KillType { get; set; }

            public DFR_BattleEntityInstanceID InstanceID { get; set; }
        }

        private sealed class RecordAttack : IDFR_BattleRecordAttack {

            public IDFR_BattleAttackSnapshot Snapshot { get; set; }

            public IDFR_BattleAttackDamage Damage { get; set; }

            public IDFR_BattleRecordHitTarget HitTarget { get; set; }

            void IDisposable.Dispose() {
                Snapshot.Dispose();
                Snapshot = null;
                Damage = null;
                HitTarget = null;
            }
        }

        private sealed class Damage : IDFR_BattleAttackDamage {

            public DFR_BattleAttackDamageType Type { get; set; }

            public F64 Value { get; set; }

            public F64 OriginValue { get; set; }
        }

        private sealed class FocusRecord : IDisposable {

            public DFR_BattleEntityInstanceID ID = DFR_BattleEntityInstanceID.Empty;

            public HashSet<DFR_BattleEntityInstanceID> TargetEntityInstanceIDSet = new();
            public HashSet<DFR_BattleEntityInstanceID> RemovingEntityInstanceIDSet = new();

            public void Dispose() {
                TargetEntityInstanceIDSet.Clear();
                RemovingEntityInstanceIDSet.Clear();
            }
        }

        private sealed class LaunchRecord : IDisposable {

            public DFR_BattleAttackID ID = DFR_BattleAttackID.Empty;

            public HashSet<DFR_BattleEntityInstanceID> PropHitTargetEntityInstanceIDSet = new();

            public void Dispose() {
                PropHitTargetEntityInstanceIDSet.Clear();
            }
        }

        private readonly Stack<FocusRecord> m_FocusPool = new();
        private readonly IDictionary<DFR_BattleEntityInstanceID, FocusRecord> m_FocusDictionary = new Dictionary<DFR_BattleEntityInstanceID, FocusRecord>();

        private readonly Stack<LaunchRecord> m_LaunchPool = new();
        private readonly IDictionary<DFR_BattleAttackID, LaunchRecord> m_LaunchDictionary = new Dictionary<DFR_BattleAttackID, LaunchRecord>();

        void IDFR_BattleAttackServiceInternal.ReleaseSnapshot(IDFR_BattleAttackSnapshot snapshot) {
            if (m_LaunchDictionary.TryGetValue(snapshot.ID, out var record)) {
                m_LaunchDictionary.Remove(snapshot.ID);
                record.Dispose();
                m_LaunchPool.Push(record);
            }
        }

        void IDFR_BattleAttackServiceInternal.ReleaseFocus(DFR_BattleEntityInstanceID instanceID) {
            if (m_FocusDictionary.TryGetValue(instanceID, out var record)) {
                m_FocusDictionary.Remove(instanceID);
                record.Dispose();
                m_FocusPool.Push(record);
            }
        }

        public IEnumerable<IDFR_BattleAttackTarget> Spotting(IDFR_BattleAttackLookout lookout) {
            Assert.IsNotNull(lookout);
            var targets = lookout.Targets;
            var results = targets.Where(item => item.IsEnabled).Where(item => IsPointInRectangle(lookout.MapPosition, lookout.SpottingRange, item.MapPosition));
            return results;
        }

        public IEnumerable<IDFR_BattleAttackTarget> Selection(IEnumerable<IDFR_BattleAttackTarget> targets, IDFR_BattleAttackTargetSelector selector, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary) {
            switch (selector.Mode) {
                case DFR_BattleAttackTargetSelectorMode.Closest:
                    return targets.OrderBy(item => Vector2F64.Distance(item.MapPosition, selector.MapPosition)).Take(selector.Number);
                case DFR_BattleAttackTargetSelectorMode.Focus:
                    return SelectFocus(targets, selector, targetModelDictionary);
                case DFR_BattleAttackTargetSelectorMode.Random:
                    return SelectRandom(targets, selector);
                case DFR_BattleAttackTargetSelectorMode.Custom:
                    return targets.OrderByDescending(item => item.IsEliteOrBoss).ThenBy(item => Vector2F64.Distance(item.MapPosition, selector.MapPosition)).Take(selector.Number);
                default:
                    UnityEngine.Debug.LogWarning($"在选择目标时 选择模式未支持({selector.Mode})");
                    return targets;
            }
        }

        private IEnumerable<IDFR_BattleAttackTarget> SelectFocus(IEnumerable<IDFR_BattleAttackTarget> selectables, IDFR_BattleAttackTargetSelector selector, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary) {
            var entityInstanceID = selector.EntityInstanceID;
            // 初始化焦点纪录
            if (!m_FocusDictionary.TryGetValue(entityInstanceID, out var record)) {
                if (!m_FocusPool.TryPop(out record)) {
                    record = new FocusRecord();
                }
                record.ID = entityInstanceID;
                m_FocusDictionary[entityInstanceID] = record;
            }
            // 检查是否存在纪录，取目标，且移除已经不存在的纪录
            var selectableDictionary = selectables.ToDictionary(item => item.EntityInstanceID);
            var number = selector.Number;
            foreach (var targetEntityInstanceID in record.TargetEntityInstanceIDSet) {
                if (targetModelDictionary.TryGetValue(targetEntityInstanceID, out var targetModel) && targetModel.AsAttackTarget().IsEnabled && selectableDictionary.ContainsKey(targetEntityInstanceID)) {
                    number--;
                } else {
                    record.RemovingEntityInstanceIDSet.Add(targetEntityInstanceID);
                }
            }
            foreach (var targetEntityInstanceID in record.RemovingEntityInstanceIDSet) {
                record.TargetEntityInstanceIDSet.Remove(targetEntityInstanceID);
            }
            // 取新目标加入纪录
            if (record.TargetEntityInstanceIDSet.Count < selector.Number) {
                var itor = selectableDictionary.Values.Where(item => !record.TargetEntityInstanceIDSet.Contains(item.EntityInstanceID)).OrderBy(item => Vector2F64.Distance(item.MapPosition, selector.MapPosition)).Take(number);
                foreach (var item in itor) {
                    record.TargetEntityInstanceIDSet.Add(item.EntityInstanceID);
                }
            }
            return record.TargetEntityInstanceIDSet.Select(item => selectableDictionary[item]);
        }

        private IEnumerable<IDFR_BattleAttackTarget> SelectRandom(IEnumerable<IDFR_BattleAttackTarget> source, IDFR_BattleAttackTargetSelector selector) {
            var count = source.Count();
            var number = selector.Number;
            if (count == 0 || number == 0) {
                yield break;
            } else if (count <= number) {
                if (count == 1) {
                    yield return source.First();
                } else {
                    foreach (var target in source) {
                        yield return target;
                    }
                }
            } else {
                var list = ListPool<IDFR_BattleAttackTarget>.Get();
                list.AddRange(source);
                for (var i = 0; i < number; i++) {
                    var index = BattleServiceBase.Instance.Random(F64.From(list.Count)).Int;
                    yield return list[index];
                    list.RemoveAt(index);
                }
                ListPool<IDFR_BattleAttackTarget>.Release(list);
            }
        }

        private static readonly Queue<IDFR_BattleAttackSnapshot> m_LaunchSnapshotQueue = new();
        private static readonly HashSet<IDFR_BattleSkillAttacker> m_LaunchSkillAttackerSet = new();
        private static readonly HashSet<IDFR_BattleAttackTarget> m_LaunchSkillKilledSet = new();
        private static readonly Dictionary<DFR_BattleEntityInstanceID, IDFR_BattleEffectModelBase> m_LaunchEffectModelDictionary = new();
        private static readonly Dictionary<DFR_BattleAttackID, IDFR_BattleAttackBulletProvider> m_LaunchBulletProviderDictionary = new();
        private static readonly HashSet<IDFR_BattleAttackTarget> m_LaunchSnapshotTargetSet = new();

        public void Launch(IDFR_BattleAttacker attacker,
                           IReadOnlyList<IDFR_BattleSkillModelBase> skillList,
                           IDFR_BattleAttackBulletProvider bulletProvider,
                           IEnumerable<IDFR_BattleAttackTarget> selectedTargets,
                           IEnumerable<IDFR_BattleAttackTarget> spottingTargets,
                           IEnumerable<IDFR_BattleAttackTarget> allTargets) {

            attacker.LaunchCount.AsReadWrite().Value++;
            var isCancelNormalAttack = false;
            foreach (var skillModel in skillList) {
                BattleSkillServiceBase.Instance.TriggerAttackLaunchSkill(skillModel, attacker, selectedTargets, (skillAttacker) => {
                    if (skillAttacker.IsCancelNormalAttack) {
                        isCancelNormalAttack = true;
                        skillAttacker.AsBulletSetter().SetBulletFlySpeed(skillAttacker.KillTargetDictionary == null ? bulletProvider.BulletFlySpeed : F64.Zero);
                    }
                    if (skillAttacker.UseOriginAttacker) {
                        skillAttacker.AsBulletSetter().SetBulletAssetKey(bulletProvider.BulletFlyAssetKey, bulletProvider.BulletHitAssetKey);
                    }
                    var effectModel = BattleSkillServiceBase.Instance.GetEffectModel(skillAttacker.EffectEntityInstanceID);
                    var effectBulletProvider = skillAttacker.AsBulletProvider();
                    var effectSnapshot = KHBD.TypeMap.Instantiate<IDFR_BattleAttackSnapshot>();
                    var influenceAmount = effectModel.AsBuffProvider().InfluenceAmountAdditional?.Value > F64.Zero ? effectModel.AsBuffProvider().InfluenceAmountAdditional.Value.Int : effectModel.InfluenceAmount;
                    effectSnapshot.Initialize(skillAttacker, attacker, true, effectModel.IsForbiddenEliteOrBoss, influenceAmount, effectBulletProvider, skillAttacker.UseOriginAttacker);
                    m_LaunchSnapshotQueue.Enqueue(effectSnapshot);
                    m_LaunchSkillAttackerSet.Add(skillAttacker);
                    m_LaunchEffectModelDictionary.Add(effectModel.EntityInstanceID, effectModel);
                    m_LaunchBulletProviderDictionary[effectSnapshot.ID] = effectBulletProvider;
                });
            }

            if (!isCancelNormalAttack) {
                var normalSnapshot = KHBD.TypeMap.Instantiate<IDFR_BattleAttackSnapshot>();
                normalSnapshot.Initialize(attacker, null, false, false, 0, bulletProvider, false);
                m_LaunchSnapshotQueue.Enqueue(normalSnapshot);
                m_LaunchBulletProviderDictionary[normalSnapshot.ID] = bulletProvider;
            }

            foreach (var snapshot in m_LaunchSnapshotQueue) {
                var killTargetDictionary = snapshot.KillTargetDictionary;
                var influenceAmount = snapshot.InfluenceAmount;
                m_LaunchEffectModelDictionary.TryGetValue(snapshot.DebuffEntityInstanceID, out var debuffEffectModel);
                // 先对主要攻击目标进行攻击
                foreach (var target in selectedTargets) {
                    if (killTargetDictionary != null && killTargetDictionary.ContainsKey(target.EntityInstanceID) && m_LaunchSkillKilledSet.Contains(target)) {
                        continue;
                    }
                    var targetBulletProvider = m_LaunchBulletProviderDictionary[snapshot.ID];
                    EmitBullet((DFR_BattleCellAxis)attacker.MapPosition, snapshot, target, targetBulletProvider, debuffEffectModel);
                    influenceAmount--; // 无论进攻成败与否，都减少一个可影响数量
                    m_LaunchSnapshotTargetSet.Add(target);
                }
                // 如果是技能效果，那么从额外的来源补充
                if (snapshot.IsEffect) {
                    var targetBulletProvider = m_LaunchBulletProviderDictionary[snapshot.ID];
                    if (killTargetDictionary != null) {
                        if (influenceAmount > 0) {
                            foreach (var target in killTargetDictionary.Values) {
                                if (influenceAmount > 0 && !m_LaunchSnapshotTargetSet.Contains(target) && !m_LaunchSkillKilledSet.Contains(target)) {
                                    var isValid = EmitBullet((DFR_BattleCellAxis)attacker.MapPosition, snapshot, target, targetBulletProvider, debuffEffectModel);
                                    if (isValid) {
                                        influenceAmount--;
                                        m_LaunchSkillKilledSet.Add(target);
                                        m_LaunchSnapshotTargetSet.Add(target);
                                    }
                                }
                            }
                        }
                    } else {
                        if (snapshot.InfluenceAmount != int.MinValue) {
                            if (influenceAmount > 0) {
                                foreach (var target in spottingTargets) {
                                    if (influenceAmount > 0 && !m_LaunchSnapshotTargetSet.Contains(target) && !m_LaunchSkillKilledSet.Contains(target)) {
                                        var isValid = EmitBullet((DFR_BattleCellAxis)attacker.MapPosition, snapshot, target, targetBulletProvider, debuffEffectModel);
                                        if (isValid) {
                                            influenceAmount--;
                                            m_LaunchSnapshotTargetSet.Add(target);
                                        }
                                    }
                                }
                            }
                        } else {
                            foreach (var target in allTargets) {
                                if (!m_LaunchSnapshotTargetSet.Contains(target)) {
                                    if (influenceAmount > 0 && !m_LaunchSnapshotTargetSet.Contains(target) && !m_LaunchSkillKilledSet.Contains(target)) {
                                        var isValid = EmitBullet((DFR_BattleCellAxis)attacker.MapPosition, snapshot, target, targetBulletProvider, debuffEffectModel);
                                        if (isValid) {
                                            m_LaunchSnapshotTargetSet.Add(target);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                if (m_LaunchSnapshotTargetSet.Count > 0) {
                    m_LaunchSnapshotTargetSet.Clear();
                    snapshot.Lightweight();
                    if (!m_LaunchPool.TryPop(out var record)) {
                        record = new LaunchRecord();
                    }
                    record.ID = snapshot.ID;
                    m_LaunchDictionary.Add(snapshot.ID, record);
                } else {
                    snapshot.Dispose();
                }
            }

            foreach (var skillAttacker in m_LaunchSkillAttackerSet) {
                if (skillAttacker is IDisposable disposable) {
                    disposable.Dispose();
                }
            }
            m_LaunchSkillKilledSet.Clear();
            m_LaunchSkillAttackerSet.Clear();
            m_LaunchSnapshotQueue.Clear();
            m_LaunchEffectModelDictionary.Clear();
            m_LaunchBulletProviderDictionary.Clear();
        }

        private bool EmitBullet(DFR_BattleCellAxis axis, IDFR_BattleAttackSnapshot snapshot, IDFR_BattleAttackTarget target, IDFR_BattleAttackBulletProvider bulletProvider, IDFR_BattleEffectModelBase effectModel) {

            if (snapshot.IsForbiddenEliteOrBoss && target.IsEliteOrBoss) {
                return false;
            }
            if (effectModel != null) {
                return BattleSkillServiceBase.Instance.AddCountdownDebuff(target.EntityInstanceID, effectModel.EntityInstanceID);
            } else {
                var localPositionProvider = BattleServiceBase.Instance.GetLocalPositionProvider(target.EntityInstanceID);
                BattleServiceBase.Instance.AttachBullet(axis, new MapPositionProvider(target), localPositionProvider, snapshot, target.EntityInstanceID, bulletProvider);
                return true;
            }
        }

        public bool Through(IDFR_BattleAttackSnapshot snapshot, DFR_BattleEntityInstanceID targetEntityInstanceID,
            DFR_BattleMapPosition lastMapPosition, DFR_BattleMapPosition mapPosition, DFR_BattleMapPosition endMapPosition,
            IDFR_ReadOnlyBattleAttackSourceDictionaryModelBase sourceModelDictionary, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary) {

            if (snapshot.Hit.Type == DFR_BattleAttackType.Prop && m_LaunchDictionary.TryGetValue(snapshot.ID, out var recrod)) {
                var range = snapshot.Hit.Range;
                var halfRange = range * F64.Half;
                var direction = (Vector2F64)endMapPosition - snapshot.LaunchMapPosition;
                var normalized = Vector2F64.Normalize(direction);
                var vector = normalized * BATTLE_BULLET_FLY_COLLISION_THRESHOLD + BATTLE_COLLISION_RECTANGLE_ADD_SIZE;
                mapPosition += vector;
                lastMapPosition -= vector;
                if (normalized != Vector2F64.Zero) {
                    var angle = F64.RadToDeg(F64.Atan2(normalized.Y, normalized.X));
                    if (angle < F64.Zero) {
                        angle += (F64)360f;
                    }
                    var p1 = MovePointByAngleAndDistance(lastMapPosition, angle - (F64)90f, halfRange);
                    var p2 = MovePointByAngleAndDistance(lastMapPosition, angle + (F64)90f, halfRange);
                    var p3 = MovePointByAngleAndDistance(mapPosition, angle + (F64)90f, halfRange);
                    var p4 = MovePointByAngleAndDistance(mapPosition, angle - (F64)90f, halfRange);
                    var propHitTargetSet = recrod.PropHitTargetEntityInstanceIDSet;
                    foreach (var kv in targetModelDictionary.Where(kv => !kv.Value.IsDead).OrderBy(item => Vector2F64.Distance(item.Value.MapPosition, mapPosition).Raw)) {
                        var targetModel = kv.Value;
                        if (propHitTargetSet.Contains(kv.Key)) {
                            continue;
                        }
                        var target = targetModel.AsAttackTarget();
                        var distance = Vector2F64.Distance(target.LastMapPosition, target.MapPosition);
                        var isCollided = CollisionCheck.IsPointInQuad(p1, p2, p3, p4, target.MapPosition);
                        if (!isCollided && distance > F64.Zero) {
                            isCollided = CollisionCheck.IsPointInQuad(p1, p2, p3, p4, target.LastMapPosition);
                            if (!isCollided && distance > BATTLE_BULLET_FLY_COLLISION_THRESHOLD) {
                                var doubleThreshold = BATTLE_BULLET_FLY_COLLISION_THRESHOLD * 2;
                                var num = distance / doubleThreshold;
                                for (var i = 1; i < num; i++) {
                                    isCollided = CollisionCheck.IsPointInQuad(p1, p2, p3, p4, Vector2F64.Lerp(target.LastMapPosition, target.MapPosition, i * doubleThreshold / distance));
                                    if (isCollided) {
                                        break;
                                    }
                                }
                            }
                        }
                        if (isCollided) {
                            propHitTargetSet.Add(kv.Key);
                            //UnityEngine.Debug.Log($"{nameof(angle)}={angle}, {nameof(p1)}={p1}, {nameof(p2)}={p2}, {nameof(p3)}={p3}, {nameof(p4)}={p4}, {nameof(target.LastMapPosition)}={target.LastMapPosition}, {nameof(target.MapPosition)}={target.MapPosition}");
                            Hit(snapshot, target, targetEntityInstanceID, true, sourceModelDictionary);
                            // 该模式下Extra额外参数指 可命中个数
                            if (propHitTargetSet.Count >= snapshot.Hit.Extra) {
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }

        private Vector2F64 MovePointByAngleAndDistance(Vector2F64 point, F64 angle, F64 distance) {
            // 角度转换为弧度
            var radian = F64.DegToRad(angle);
            // 计算新的位置
            var newX = point.X + F64.Cos(radian) * distance;
            var newY = point.Y + F64.Sin(radian) * distance;
            return new Vector2F64(newX, newY);
        }

        public void Hit(IDFR_BattleAttackSnapshot snapshot, DFR_BattleEntityInstanceID targetEntityInstanceID, IDFR_BattleMapPositionProvider targetMapPositionProvider,
            IDFR_ReadOnlyBattleAttackSourceDictionaryModelBase sourceModelDictionary, IDFR_ReadOnlyBattleAttackTargetDictionaryModelBase targetModelDictionary) {
            switch (snapshot.Hit.Type) {
                case DFR_BattleAttackType.Point:
                    if (targetModelDictionary.TryGetValue(targetEntityInstanceID, out var targetModel_Point)) {
                        Hit(snapshot, targetModel_Point.AsAttackTarget(), targetEntityInstanceID, true, sourceModelDictionary);
                    }
                    break;
                case DFR_BattleAttackType.Radius:
                    var range = snapshot.Hit.Range;
                    foreach (var kv in targetModelDictionary.OrderBy(kv => kv.Key.Index)) {
                        var distance = Vector2F64.Distance(kv.Value.AsAttackTarget().MapPosition, targetMapPositionProvider.MapPosition);
                        if (distance <= range) {
                            Hit(snapshot, kv.Value.AsAttackTarget(), targetEntityInstanceID, true, sourceModelDictionary);
                        }
                    }
                    break;
                case DFR_BattleAttackType.Prop:
                    break;
                case DFR_BattleAttackType.DOT:
                    if (targetModelDictionary.TryGetValue(targetEntityInstanceID, out var targetModel_DOT)) {
                        Hit(snapshot, targetModel_DOT.AsAttackTarget(), targetEntityInstanceID, false, sourceModelDictionary);
                    }
                    break;
                default:
                    UnityEngine.Debug.LogWarning($"在命中目标时 命中类型未支持({snapshot.Hit.Type})");
                    break;
            }
        }

        private void Hit(IDFR_BattleAttackSnapshot snapshot, IDFR_BattleAttackTarget target, DFR_BattleEntityInstanceID mainTargetEntityInstanceID, bool hasTrigger, IDFR_ReadOnlyBattleAttackSourceDictionaryModelBase sourceModelDictionary) {

            sourceModelDictionary.TryGetValue(snapshot.SourceEntityInstanceID, out var sourceModel);
            if (target.IsEnabled) {
                if (hasTrigger && sourceModel != null) {
                    foreach (var skillModel in sourceModel.SkillList.Where(item => item.TriggerTiming == DFR_BattleSkillTriggerTiming.Hit)) {
                        BattleSkillServiceBase.Instance.TriggerAttackHitSkill(skillModel, snapshot.SourceEntityInstanceID, target);
                    }
                }
            }

            // Hit Skill触发后可能已经死亡，需要再一次判断是否可用
            if (target.IsEnabled) {
                var isKill = Underattack(snapshot, target, mainTargetEntityInstanceID);
                if (!string.IsNullOrWhiteSpace(snapshot.BulletHitAssetKey)) {
                    BattleServiceBase.Instance.AttachObject(snapshot.BulletHitAssetKey, target.MapPosition, BATTLE_HIT_AUTO_KILL_TIME);
                }
                if (hasTrigger && isKill && sourceModel != null) {
                    foreach (var skillModel in sourceModel.SkillList.Where(item => item.TriggerTiming == DFR_BattleSkillTriggerTiming.Kill)) {
                        BattleSkillServiceBase.Instance.TriggerAttackKillSkill(skillModel, snapshot.SourceEntityInstanceID, target);
                    }
                }
            }
        }

        public bool Underattack(IDFR_BattleAttackSnapshot snapshot, IDFR_BattleAttackTarget target, DFR_BattleEntityInstanceID mainTargetEntityInstanceID) {
            var attack = snapshot.Attack;
            var damageAttack = snapshot.DamageAttack;
            if (target.IsEliteOrBoss) {
                attack += snapshot.AttackBossAdditional;
                damageAttack += snapshot.DamageAttackBossAdditional;
            }
            var defender = target.AsAttackDefender();
            var (damage, isKill) = Underattack(snapshot, new Damage {
                // 普通攻击携带的damage比例从100%开始，技能携带的damage与damageHP以及damageCurrentHP比例从0%开始, 
                Value = attack * snapshot.Damage + damageAttack * snapshot.Damage + defender.HPMax * snapshot.DamageHPMax + defender.HP * snapshot.DamageHP,
                Type = DFR_BattleAttackDamageType.Normal,
            }, defender, target.EntityInstanceID == mainTargetEntityInstanceID);
            if (target.DoUnderattack(damage, out var killType)) {
                var hitTarget = new HitTarget { InstanceID = target.EntityInstanceID, KillType = killType, IsKill = isKill };
                var record = new RecordAttack { Snapshot = snapshot, Damage = damage, HitTarget = hitTarget };
                BattleRecordService.Writter.Submit(record);
            }
            return isKill;
        }

        private (IDFR_BattleAttackDamage, bool) Underattack(IDFR_BattleAttackSnapshot snapshot, IDFR_BattleAttackDamage arg, IDFR_BattleAttackDefender defender, bool isMainTarget) {
            var damage = (Damage)arg;
            if (!snapshot.IsEffect) {
                if (damage.Value <= defender.Defense) {
                    damage.Value = F64.Zero;
                } else {
                    damage.Value -= defender.Defense;
                }
                if (!isMainTarget && snapshot.Hit.Type == DFR_BattleAttackType.Radius) {
                    damage.Value *= snapshot.Hit.Extra;
                }
                if (snapshot.Critical >= F64.One || BattleServiceBase.Instance.Random() < snapshot.Critical) {
                    damage.Value *= snapshot.CriticalPower;
                    damage.Type = DFR_BattleAttackDamageType.Critical;
                }
                if (damage.Type != DFR_BattleAttackDamageType.Critical) {
                    if (defender.Dodge >= F64.One || BattleServiceBase.Instance.Random() < defender.Dodge) {
                        damage.Type = DFR_BattleAttackDamageType.Dodge;
                        damage.Value = F64.Zero;
                        return (damage, false);
                    }
                }
                if (damage.Value == F64.Zero) {
                    damage.Type = DFR_BattleAttackDamageType.Block;
                    return (damage, false);
                }
            }
            damage.OriginValue = damage.Value;
            if (defender.IsEnabled) {
                if (damage.Value > defender.HP) {
                    damage.Value = defender.HP;
                }
                var isKill = damage.Value >= defender.HP;
                return (damage, isKill);
            } else {
                damage.Value = F64.Zero;
                return (damage, false);
            }
        }

        void IService.OnApplicationQuit() {
            m_FocusDictionary.Clear();
            m_FocusPool.Clear();
            m_LaunchDictionary.Clear();
            m_LaunchPool.Clear();
            m_LaunchSnapshotQueue.Clear();
            m_LaunchSkillAttackerSet.Clear();
            m_LaunchEffectModelDictionary.Clear();
            m_LaunchBulletProviderDictionary.Clear();
            m_LaunchSnapshotTargetSet.Clear();
            m_LaunchSkillKilledSet.Clear();
        }
    }
}

namespace Internal.Runtime.Services.BattleAttack {

    public interface IDFR_BattleAttackServiceInternal {

        void ReleaseSnapshot(IDFR_BattleAttackSnapshot snapshot);

        void ReleaseFocus(DFR_BattleEntityInstanceID instanceID);
    }
}
